import re
from typing import Any, Dict, List, Optional

from cold_start_analyzer import phase_helper


def _to_number(value: Any) -> Optional[float]:
    if isinstance(value, (int, float)):
        return float(value)
    return None


def _format_ms(value: Optional[float]) -> str:
    if value is None:
        return "N/A"
    return f"{value / 1_000_000:.2f}"


def compare_phase_breakdown(
    phases_a: Optional[Dict[str, Any]],
    phases_b: Optional[Dict[str, Any]],
) -> Dict[str, List[Dict[str, Any]]]:
    flat_a = phase_helper.flatten_phase_tree(phases_a, include_root=False)
    flat_b = phase_helper.flatten_phase_tree(phases_b, include_root=False)

    def build_map(rows: List[Dict[str, Any]]) -> Dict[str, Dict[str, Any]]:
        result = {}
        for row in rows:
            key = f"{row.get('path')}|{row.get('name')}"
            result[key] = row
        return result

    map_a = build_map(flat_a)
    map_b = build_map(flat_b)
    all_keys = set(map_a.keys()) | set(map_b.keys())
    added: List[Dict[str, Any]] = []
    removed: List[Dict[str, Any]] = []
    changed: List[Dict[str, Any]] = []

    for key in sorted(all_keys):
        node_a = map_a.get(key)
        node_b = map_b.get(key)
        if node_a and not node_b:
            cost_ns = _to_number(node_a.get("cost"))
            added.append(
                {
                    "path": node_a.get("path"),
                    "name": node_a.get("name"),
                    "cost_ns": cost_ns,
                    "cost_ms": _format_ms(cost_ns),
                }
            )
            continue
        if node_b and not node_a:
            cost_ns = _to_number(node_b.get("cost"))
            removed.append(
                {
                    "path": node_b.get("path"),
                    "name": node_b.get("name"),
                    "cost_ns": cost_ns,
                    "cost_ms": _format_ms(cost_ns),
                }
            )
            continue
        if not node_a or not node_b:
            continue
        cost_a = _to_number(node_a.get("cost"))
        cost_b = _to_number(node_b.get("cost"))
        if cost_a is None and cost_b is None:
            continue
        if cost_a is None:
            added.append(
                {
                    "path": node_b.get("path"),
                    "name": node_b.get("name"),
                    "cost_ns": cost_b,
                    "cost_ms": _format_ms(cost_b),
                }
            )
            continue
        if cost_b is None:
            removed.append(
                {
                    "path": node_a.get("path"),
                    "name": node_a.get("name"),
                    "cost_ns": cost_a,
                    "cost_ms": _format_ms(cost_a),
                }
            )
            continue
        if cost_a == cost_b:
            continue
        diff = None
        diff_percent = None
        if cost_a is not None and cost_b is not None:
            diff = cost_b - cost_a
            if cost_a != 0:
                diff_percent = (diff / cost_a) * 100
        changed.append(
            {
                "path": node_a.get("path"),
                "name": node_a.get("name"),
                "cost_a_ns": cost_a,
                "cost_a_ms": _format_ms(cost_a),
                "cost_b_ns": cost_b,
                "cost_b_ms": _format_ms(cost_b),
                "diff_ns": diff,
                "diff_ms": _format_ms(diff if diff is not None else None),
                "diff_percent": f"{diff_percent:.2f}%" if diff_percent is not None else "N/A",
            }
        )

    added.sort(key=lambda item: (item.get("path") or ""))
    removed.sort(key=lambda item: (item.get("path") or ""))
    return {
        "changed": changed,
        "added": added,
        "removed": removed,
    }

_name_normalizer = re.compile(r"\d+")

def _normalize_name(name: str) -> str:
    return _name_normalizer.sub("", name or "")


def compare_table_items(
    rows_a: Optional[List[Dict[str, Any]]],
    rows_b: Optional[List[Dict[str, Any]]],
) -> List[Dict[str, Any]]:
    def _to_map(rows: Optional[List[Dict[str, Any]]]) -> Dict[str, Dict[str, Any]]:
        if not rows:
            return {}
        result = {}
        for index, row in enumerate(rows):
            raw_name = row.get("name") or f"row_{index}"
            key = _normalize_name(raw_name)
            result[key] = row
        return result

    map_a = _to_map(rows_a)
    map_b = _to_map(rows_b)
    keys = set(map_a.keys()) | set(map_b.keys())
    results: List[Dict[str, Any]] = []
    for key in keys:
        row_a = map_a.get(key)
        row_b = map_b.get(key)
        cost_a = row_a.get("cost") if row_a else None
        cost_b = row_b.get("cost") if row_b else None
        if cost_a == cost_b:
            continue
        diff = None
        if isinstance(cost_a, (int, float)) and isinstance(cost_b, (int, float)):
            diff = cost_b - cost_a
        results.append(
            {
                "name": row_b.get("name") if row_b else row_a.get("name"),
                "cost_a_ms": _format_ms(cost_a),
                "cost_b_ms": _format_ms(cost_b),
                "diff_ms": _format_ms(diff if diff is not None else None),
                "diff_abs": abs(diff) if diff is not None else None,
                "cost_a_raw": cost_a,
                "cost_b_raw": cost_b,
                "diff_raw": diff,
            }
        )
    results.sort(key=lambda item: item.get("diff_abs") or 0, reverse=True)
    return results
